package com.pnnd.stream;

import java.io.EOFException;
import java.io.IOException;
import java.io.OutputStream;

public class SingleSegmentOutputStream extends OutputStream {

    private int start;
    private int offset;
    private MemorySegment memorySegment;

    public SingleSegmentOutputStream(MemorySegment memorySegment){
        this.memorySegment = memorySegment;
        this.offset = this.start = 0;
    }

    public SingleSegmentOutputStream(MemorySegment memorySegment,int offset){
        this.memorySegment = memorySegment;
        this.offset = offset;
        this.start = 0;
    }

    @Override
    public void write(int b) throws IOException {

        if(this.memorySegment.start() + offset + 1 > this.memorySegment.end()){
            throw new EOFException();
        }

        int writePos = this.getWritePos();

        this.memorySegment.put(writePos,(byte) b);

        this.offset++;
    }

    @Override
    public void write(byte b[]) throws IOException {

        if(b == null){
            throw new NullPointerException();
        }

        if(this.memorySegment.start() + b.length + this.offset > this.memorySegment.end()){
            throw new EOFException();
        }

        this.memorySegment.put(this.getWritePos(),b);

        this.offset += b.length;
    }

    @Override
    public void write(byte b[], int off, int len) throws IOException {
        if(b == null){
            throw new NullPointerException();
        }

        if(this.memorySegment.start() + len + this.offset > this.memorySegment.end()){
            throw new EOFException();
        }

        this.memorySegment.put(this.getWritePos(),b,off,len);

        this.offset += len;
    }

    public int available(){
        return (int) (this.memorySegment.end() - this.offset);
    }

    public boolean canWrote(int size){
        return this.memorySegment.canWrote(size);
    }

    /**
     * 计算当前流可写入未知
     * @return int
     */
    protected int getWritePos(){
        return this.offset + (int)this.memorySegment.start();
    }

    /**
     * Flushes this output stream and forces any buffered output bytes
     * to be written out. The general contract of <code>flush</code> is
     * that calling it is an indication that, if any bytes previously
     * written have been buffered by the implementation of the output
     * stream, such bytes should immediately be written to their
     * intended destination.
     * <p>
     * If the intended destination of this stream is an abstraction provided by
     * the underlying operating system, for example a file, then flushing the
     * stream guarantees only that bytes previously written to the stream are
     * passed to the operating system for writing; it does not guarantee that
     * they are actually written to a physical device such as a disk drive.
     * <p>
     * The <code>flush</code> method of <code>OutputStream</code> does nothing.
     *
     * @exception  IOException  if an I/O error occurs.
     */
    @Override
    public void flush() throws IOException {
        Object owner = this.memorySegment.getOwner();

        if(owner instanceof MemorySegmentContainer){
            ((MemorySegmentContainer) owner).refresh(this.offset);
        }

        this.start = this.offset;
    }


    public int getStart() {
        return start;
    }
}
